﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using Interfaces;

namespace Pozr
{
    public class PluginServices : IMiddleware
    {
        private string pluginDirPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\plugins";
        public List<string> ModuleList;

        public PluginServices()
        {
            ModuleList = new List<string>();
        }

        public List<string> GetActiveModules()
        {
            List<string> l = new List<string>();
            foreach (Types.Plugin plugin in colPlugins)
            {
                l.Add(plugin.Instance.Name);
            }
            return l;
        }

        #region StandardServices
        /// <summary>
        /// Constructor of the Class
        /// </summary>
             
        private Types.Plugins colPlugins = new Types.Plugins();

        /// <summary>
        /// A collection of Plugins found and loaded by the FindPlugins() method
        /// </summary>
        public Types.Plugins Plugins
        {
            get { return colPlugins; }
            set { colPlugins = value; }
        }

        /// <summary>
        /// Searches the application's startup directory for plugins
        /// </summary>
        public void LoadModules()
        {
            FindPlugins(pluginDirPath);
        }

        /// <summary>
        /// Searches the passed path for plugins
        /// </summary>
        /// <param name="path">Directory to search for plugins in</param>
        public void FindPlugins(string path)
        {
            //Empty the collection
            colPlugins.Clear();

            //Go through all the files in the plugin directory
            try
            {
                foreach (string fileOn in Directory.GetFiles(path))
                {
                    FileInfo file = new FileInfo(fileOn);

                    //It must be .dll
                    if (file.Extension.Equals(".dll"))
                    {
                        //Add the 'plugin'
                        this.AddPlugin(fileOn);
                    }
                }
            }
            catch (DirectoryNotFoundException ex)
            {
                Middleware.WriteLogToFile(ex.Message);
            }
        }

        public bool ClosePlugin(string name)
        {
            Types.Plugin plugin = colPlugins.Find(name);
            if (plugin != null)
            {
                colPlugins.Remove(plugin);
                plugin.Instance.Dispose();
                plugin.Instance = null;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Unloads and closes all Plugins
        /// </summary>
        public void ClosePlugins()
        {
            foreach (Types.Plugin pluginOn in colPlugins)
            {
                //Close all plugin instances
                pluginOn.Instance.Dispose();
                pluginOn.Instance = null;
            }

            //Clear our collection of plugins
            colPlugins.Clear();
        }

        private void AddPlugin(string FileName)
        {
            //Create a new assembly from the plugin file we're adding..
            Assembly pluginAssembly = Assembly.LoadFrom(FileName);

            //Next we'll loop through all the Types found in the assembly
            foreach (Type pluginType in pluginAssembly.GetTypes())
            {
                if (pluginType.IsPublic) //Only look at public types
                {
                    if (!pluginType.IsAbstract)  //Only look at non-abstract types
                    {
                        Type typeInterface = null;

                        //Gets a type object of the interface we need the plugins to match
                        foreach (Type t in pluginType.GetInterfaces())
                        {
                            if (t.FullName == "Interfaces.IApplication")
                                typeInterface = t;
                        }

                        //Make sure the interface we want to use actually exists
                        if (typeInterface != null)
                        {
                            //Create a new plugin since the type implements the IPlugin interface
                            Types.Plugin newPlugin = new Types.Plugin();

                            //Set the filename where we found it
                            newPlugin.AssemblyPath = FileName;

                            //Create a new instance and store the instance in the collection for later use
                            newPlugin.Instance = (IApplication)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));

                            //Set the Plugin's host to this class which inherited IPluginMiddleware
                            newPlugin.Instance.Host = this;

                            //Call the initialization sub of the plugin
                            newPlugin.Instance.Initialize();

                            //Add the new plugin to our collection here
                            this.colPlugins.Add(newPlugin);

                            ModuleList.Add(newPlugin.Instance.Name);

                            //Cleanup
                            newPlugin = null;
                        }

                        typeInterface = null;			
                    }
                }
            }

            pluginAssembly = null;
        }

        #endregion

        #region IMiddleware Members

        #region Events

        public event NewAbsoluteDataAvailableHandler NewAbsoluteDataAvailable;

        public event NewCellDataAvailableHandler NewCellDataAvailable;

        public event NewWifiDataAvailableHandler NewWifiDataAvailable;

        public event NewPairedDataAvailableHandler NewPairedDataAvailable;

        public event ContextChangedHandler ContextChanged;

        #endregion

        #region Marker handling

        public int CreateTemplate(System.IO.Stream background, System.Drawing.Point imageOffset, System.Drawing.Point textOffset, string owner)
        {
            return Middleware.Markers.AddProto(background, imageOffset, textOffset, owner);
        }

        public int NewMarker(double lat, double lon, int proto_id, string text, System.Drawing.Bitmap image)
        {
            return Middleware.Markers.Add(lat, lon, proto_id, text, image);
        }

        public bool MoveMarker(int marker_id, double latitude, double longitude)
        {
            return Middleware.Markers.Move(marker_id, latitude, longitude);
        }

        #endregion

        #region Event handling

        public int CreateEvent(string description, short minRelevance, ContextMatchesHandler eventHandler, PositionOriginator[] originators)
        {
            return Middleware.EventEngine.AddEvent(description, minRelevance ,eventHandler, originators);
        }

        public int CreateEvent(string description, short minRelevance, ContextMatchesHandler eventHandler, string[] members)
        {
            return Middleware.EventEngine.AddEvent(description, minRelevance, eventHandler, members);
        }

        public bool MergeEvent(int id, string[] members)
        {
            return Middleware.EventEngine.MergeEvent(id, members);
        }

        public bool RemoveEvent(int _id)
        {
            return Middleware.EventEngine.RemoveEvent(_id);
        }

        public string[] GetCurrentContext(PositionOriginator[] originators)
        {
            return Middleware.PositionHandler.GetCurrentContextHashes(originators);
        }

        #endregion

        #endregion

        #region ICommonInterface Members

        public bool InitSettingsGroup(string app_id)
        {
            return Middleware.Settings.InitSettingsGroup(app_id);
        }

        public bool DeleteSettingsGroup(string app_id)
        {
            return Middleware.Settings.DeleteSettingsGroup(app_id);
        }

        public bool SettingsGroupAvailable(string app_id)
        {
            return Middleware.Settings.SettingsGroupAvailable(app_id);
        }

        public bool SetSetting(string app_id, string setting_name, object data)
        {
            return Middleware.Settings.SetSetting(app_id, setting_name, data);
        }

        public bool DeleteSetting(string app_id, string setting_name)
        {
            return Middleware.Settings.DeleteSetting(app_id, setting_name);
        }

        public object GetSetting(string app_id, string setting_name)
        {
            return Middleware.Settings.GetSetting(app_id, setting_name);
        }

        #endregion

        #region Methods

        public void OnNewAbsoluteDataAvailable(object sender, IAbsoluteData data)
        {
            if (NewAbsoluteDataAvailable != null)
            {
                NewAbsoluteDataAvailable(sender, data);
            }
        }

        public void OnNewCellDataAvailable(object sender, CellData data)
        {
            if (NewCellDataAvailable != null)
            {
                NewCellDataAvailable(sender, data);
                OnContextChanged(sender);
            }
        }

        public void OnNewWifiDataAvailable(object sender, WifiData[] data)
        {
            if (NewWifiDataAvailable != null)
            {
                NewWifiDataAvailable(sender, data);
                OnContextChanged(sender);
            }
        }
        public void OnPairedDataAvailable(object sender, IAbsoluteData absoluteData, IProximityData proximityData)
        {
            if (NewPairedDataAvailable != null)
            {
                NewPairedDataAvailable(sender, absoluteData, proximityData);
            }
        }

        public void OnContextChanged(object sender)
        {
            if (ContextChanged != null)
            {
                ContextChanged(sender);
            }
        }

        public void WriteDebugLog(string s)
        {
            Middleware.OnLogEvent(s);
        }

        public string WorkingDirectory
        {
            get { return Middleware.ApplicationDirectory; }
        }

        #endregion
    }

    namespace Types
    {
        /// <summary>
        /// Collection for Plugin type
        /// </summary>
        public class Plugins : System.Collections.CollectionBase
        {
            /// <summary>
            /// Add a Plugin to the collection of plugins
            /// </summary>
            /// <param name="pluginToAdd">The Plugin to Add</param>
            public void Add(Types.Plugin pluginToAdd)
            {
                this.List.Add(pluginToAdd);
            }

            /// <summary>
            /// Remove a Plugin from the collection of plugins
            /// </summary>
            /// <param name="pluginToRemove">The Plugin to Remove</param>
            public void Remove(Types.Plugin pluginToRemove)
            {
                this.List.Remove(pluginToRemove);
            }

            /// <summary>
            /// Finds a plugin in the available Plugins
            /// </summary>
            /// <param name="pluginNameOrPath">The name or file path of the plugin to find</param>
            /// <returns>Plugin, or null if the plugin is not found</returns>
            public Types.Plugin Find(string pluginNameOrPath)
            {
                Types.Plugin toReturn = null;

                //Loop through all the plugins
                foreach (Types.Plugin pluginOn in this.List)
                {
                    //Find the one with the matching name or filename
                    if ((pluginOn.Instance.Name.Equals(pluginNameOrPath)) || pluginOn.AssemblyPath.Equals(pluginNameOrPath))
                    {
                        toReturn = pluginOn;
                        break;
                    }
                }
                return toReturn;
            }
        }

        /// <summary>
        /// Data class for Plugin.
        /// Holds and instance of the loaded Plugin, as well as the Plugin's assembly path
        /// </summary>
        public class Plugin
        {
            //This is the actual Plugin object.. 
            //Holds an instance of the plugin to access
            //ALso holds assembly path... not really necessary
            private IApplication myInstance = null;
            private string myAssemblyPath = "";

            public IApplication Instance
            {
                get { return myInstance; }
                set { myInstance = value; }
            }
            public string AssemblyPath
            {
                get { return myAssemblyPath; }
                set { myAssemblyPath = value; }
            }
        }
    }
}
